/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

json *
submitrequest_2json(struct QuickPoolJobInfo *job)
{
    struct submitRequest *submitInfo = job->submit;

    json *jsonJobSubmit = json_CreateObject();

    if (submitInfo->user != NULL && submitInfo->user[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "USER", user);
    }
    if (submitInfo->queue != NULL && submitInfo->queue[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "QUEUE", queue);
    }

    char *strJobName, *pos;
    strJobName = submitInfo->job_name;
    if (ARRAY_INDEX(job->jobid) && (pos = strchr(strJobName, '['))) {
        *pos = '\0';
        snprintf(strJobName, MAXNAMELEN, "%s[%d]", strJobName, ARRAY_INDEX(job->jobid));
    }
    if (strJobName != NULL && strJobName[0] != '\0') {
        json_AddStringToObject(jsonJobSubmit, "JOB_NAME", strJobName);
    }

    if (NULL != submitInfo->project && '\0' != submitInfo->project[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "PROJECT", project);
    }

    if (NULL != submitInfo->user_group && '\0' != submitInfo->user_group[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "USER_GROUP", user_group);
    }

    if (NULL != submitInfo->mail_user && '\0' != submitInfo->mail_user[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "MAIL_USER", mail_user);
    }

    if (NULL != submitInfo->job_group && '\0' != submitInfo->job_group[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "JOB_GROUP", job_group);
    }

    if (submitInfo->from_node != NULL && submitInfo->from_node[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "FROM_HOST", from_node);
    }

    if (submitInfo->options & SUBOPT_EXCLUSIVE) {
        json_AddTrueToObject(jsonJobSubmit, "EXCLUSIVE");
    }

    if (submitInfo->options & SUBOPT_NOTIFY_BEGIN) {
        json_AddTrueToObject(jsonJobSubmit, "NOTIFY_BEGIN");
    }

    if (submitInfo->options & SUBOPT_NOTIFY_END) {
        json_AddTrueToObject(jsonJobSubmit, "NOTIFY_END");
    }

    if (submitInfo->options & SUBOPT_RERUNNABLE) {
        json_AddTrueToObject(jsonJobSubmit, "RERUNNABLE");
    }

    if (submitInfo->options & SUBOPT_RESTART) {
        json_AddTrueToObject(jsonJobSubmit, "RESTART");
    }

    if (submitInfo->options2 & SUBOPT2_HOLD) {
        json_AddTrueToObject(jsonJobSubmit, "ON_HOLD");
    }

    if (submitInfo->num_job_slot == submitInfo->max_job_slot) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, int, "NSLOT", num_job_slot);
    } else {
        char strSlot[32+1];
        snprintf(strSlot, 32, "%d, %d", submitInfo->num_job_slot, submitInfo->max_job_slot);

        json_AddStringToObject(jsonJobSubmit, "NSLOT", strSlot);
    }

    char timebuf[TIMESTRINGLEN];
    ctime_2string(CTIME_FORMAT_a_b_d_T_Y, &submitInfo->submit_time, timebuf);
    json_AddStringToObject(jsonJobSubmit, "SUBMIT_TIME", timebuf);

    if (NULL != submitInfo->resource_requirement && '\0' != submitInfo->resource_requirement[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "RES_REQ", resource_requirement);
    }

    if (NULL != submitInfo->dependency && '\0' != submitInfo->dependency[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "DEPENDENCY", dependency);
    }

    if (0 < submitInfo->begin_time) {
        ctime_2string(CTIME_FORMAT_a_b_d_T_Y, &(submitInfo->begin_time), timebuf);
        json_AddStringToObject(jsonJobSubmit, "BEGIN_TIME", timebuf);
    }

    if (0 < submitInfo->terminate_time) {
        ctime_2string(CTIME_FORMAT_a_b_d_T_Y, &(submitInfo->terminate_time), timebuf);
        json_AddStringToObject(jsonJobSubmit, "TERMINATE_TIME", timebuf);
    }

    if (NULL != submitInfo->login_shell && '\0' != submitInfo->login_shell[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "LOGIN_SHELL", login_shell);
    }

    if (NULL != submitInfo->prejob_command && '\0' != submitInfo->prejob_command[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "PRE_EXEC", prejob_command);
    }

    if (NULL != submitInfo->postjob_command && '\0' != submitInfo->postjob_command[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "POST_EXEC", postjob_command);
    }

    if (0 < submitInfo->num_spec_node) {
        s2j_json_set_array_element(jsonJobSubmit, submitInfo, string, "SPEC_HOSTS", spec_nodes, submitInfo->num_spec_node);
    }

    if (NULL != submitInfo->chkpnt_dir && '\0' != submitInfo->chkpnt_dir[0]) {
        char *strChkPnt;
        unsigned int len = strlen(submitInfo->chkpnt_dir) + 20;
        if (NULL != submitInfo->chkpnt_method && '\0' != submitInfo->chkpnt_method[0]) {
            len += strlen(submitInfo->chkpnt_method);
        }

        strChkPnt = (char *)calloc(len+1, sizeof(char));
        if (NULL == strChkPnt) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len);
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        if (submitInfo->chkpnt_duration > 0 
            && NULL != submitInfo->chkpnt_method && '\0' != submitInfo->chkpnt_method[0]) {

            snprintf(strChkPnt, len, "%s %d [method=%s]",
                     submitInfo->chkpnt_dir, submitInfo->chkpnt_duration,
                     submitInfo->chkpnt_method);
        } else {
            snprintf(strChkPnt, len, "%s", submitInfo->chkpnt_dir);
        }

        json_AddStringToObject(jsonJobSubmit, "CHECK_POINT", strChkPnt);
        FREEUP(strChkPnt);
    }

    if (0 < submitInfo->spec_priority) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, int, "USER_PRIORITY", spec_priority);
    }

    if (0 < submitInfo->window_signal) {
        json_AddStringToObject(jsonJobSubmit, "WINDOW_SIGNAL", gf_signal_2string(submitInfo->window_signal));
    }

    unsigned int limit;
    for (limit=0; limit<RESOURCE_LIMIT_NUM; limit++) {
        char strLimit[32];
        memset(strLimit, 0, 32);

        switch (limit) {
	case RESOURCE_LIMIT_CPU:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_CPU] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_CPU] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_CPU]);
                json_AddStringToObject(jsonJobSubmit, "CPULIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_RUN:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_RUN] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_RUN] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_RUN]);
                json_AddStringToObject(jsonJobSubmit, "RUNLIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_DATA:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_DATA] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_DATA] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_DATA]);
                json_AddStringToObject(jsonJobSubmit, "DATALIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_FSIZE:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_FSIZE] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_FSIZE] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_FSIZE]);
                json_AddStringToObject(jsonJobSubmit, "FILELIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_STACK:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_STACK] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_STACK] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_STACK]);
                json_AddStringToObject(jsonJobSubmit, "STACKLIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_CORE:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_CORE] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_CORE] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_CORE]);
                json_AddStringToObject(jsonJobSubmit, "CORELIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_RSS:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_RSS] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_RSS] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_RSS]);
                json_AddStringToObject(jsonJobSubmit, "MEMLIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_SWAP:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_SWAP] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_SWAP] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_SWAP]);
                json_AddStringToObject(jsonJobSubmit, "SWAPLIMIT", strLimit);
            }

            break;
	case RESOURCE_LIMIT_PROCESS:
            if (submitInfo->job_ulimit[RESOURCE_LIMIT_PROCESS] != INFINIT_INT
                && submitInfo->job_ulimit[RESOURCE_LIMIT_PROCESS] > 0) {

                snprintf(strLimit, 32, "%d", submitInfo->job_ulimit[RESOURCE_LIMIT_PROCESS]);
                json_AddStringToObject(jsonJobSubmit, "PROCESSLIMIT", strLimit);
            }

            break;
        }
    }

    if (submitInfo->cwd != NULL) {
        if (submitInfo->cwd[0] == '/' || submitInfo->cwd[0] == '\\'
            || (submitInfo->cwd[0] != '\0' && submitInfo->cwd[1] == ':')) {

            s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "CWD", cwd);
        } else if (submitInfo->cwd[0] == '\0') {

            s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "CWD", home_dir);
        } else {
            char strCwd[PATH_MAX+1];
            snprintf(strCwd, PATH_MAX, "%s/%s", submitInfo->home_dir, submitInfo->cwd);
            json_AddStringToObject(jsonJobSubmit, "CWD", strCwd);
        }
    }

    if (submitInfo->home_dir != NULL && submitInfo->home_dir[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "HOMEDIR", home_dir);
    }

    if (NULL != submitInfo->input_file && '\0' != submitInfo->input_file[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "INPUT_FILE", input_file);
    }

    if (NULL != submitInfo->output_file && '\0' != submitInfo->output_file[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "OUTPUT_FILE", output_file);
    }

    if (NULL != submitInfo->error_file && '\0' != submitInfo->error_file[0]) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "ERROR_FILE", error_file);
    }

    if (0 < submitInfo->num_exec_files) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, int, "NUM_COPY_FILE", num_exec_files);
    }

    unsigned int i;
    for (i = 0; i < submitInfo->num_exec_files; i++) {
        char strCopyFile[PATH_MAX+1];

        if (submitInfo->exec_files[i].direction & XF_OP_SUB2EXEC_APPEND) {
            snprintf(strCopyFile, PATH_MAX, "%s >> %s", submitInfo->exec_files[i].in_file,
                     submitInfo->exec_files[i].out_file);
        } if (submitInfo->exec_files[i].direction & XF_OP_EXEC2SUB_APPEND) {
            snprintf(strCopyFile, PATH_MAX, "%s << %s", submitInfo->exec_files[i].in_file,
                     submitInfo->exec_files[i].out_file);
        } else if (submitInfo->exec_files[i].direction & XF_OP_SUB2EXEC
                   && submitInfo->exec_files[i].direction & XF_OP_EXEC2SUB) {
            snprintf(strCopyFile, PATH_MAX, "%s >< %s", submitInfo->exec_files[i].in_file,
                     submitInfo->exec_files[i].out_file);
        } else if (submitInfo->exec_files[i].direction & XF_OP_EXEC2SUB) {
            snprintf(strCopyFile, PATH_MAX, "%s < %s", submitInfo->exec_files[i].in_file,
                     submitInfo->exec_files[i].out_file);
        } else {
            snprintf(strCopyFile, PATH_MAX, "%s > %s", submitInfo->exec_files[i].in_file,
                     submitInfo->exec_files[i].out_file);
        }

        json_AddStringToObject(jsonJobSubmit, "COPY_FILE", strCopyFile);
    }

    if (submitInfo->command != NULL && submitInfo->command[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "COMMAND", command);
    }

    if (0 < submitInfo->num_gpu) {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, int, "GPU_NUM", num_gpu);

        if (!FLOAT_EQUAL_ZERO(submitInfo->gpu_mem)) {
            double memory;
            char *strUnit;
            if (1024*1024 < submitInfo->gpu_mem) {
                memory = (double)(submitInfo->gpu_mem)/(double)(1024*1024);
                strUnit = (char *)"T";
            } else if (1024 < submitInfo->gpu_mem) {
                memory = (double)(submitInfo->gpu_mem)/(double)(1024);
                strUnit = (char *)"G";
            } else {
                strUnit = (char *)"M";
                memory = submitInfo->gpu_mem;
            }

            char strMem[56+1];
            snprintf(strMem, 56, "%.1f%s", memory, strUnit);
            json_AddStringToObject(jsonJobSubmit, "GPU_MEM", strMem);
        }

        if (0 < submitInfo->gpu_exclusive) {
            json_AddTrueToObject(jsonJobSubmit, "GPU_EXCL");
        }

        if (NULL != submitInfo->gpu_model && '\0' != submitInfo->gpu_model[0]) {
            s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "GPU_MODEL", gpu_model);
        }

        if (NULL != submitInfo->gpu_mps && '\0' != submitInfo->gpu_mps[0]) {
            s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "GPU_MPS", gpu_mps);
        }
    }

    if (submitInfo->requeue_exitcode != NULL &&  submitInfo->requeue_exitcode[0] != '\0') {
        s2j_json_set_basic_element(jsonJobSubmit, submitInfo, string, "REQUEUE_EXITCODE", requeue_exitcode);
    }

    return jsonJobSubmit;
} // end function submitrequest_2json

/**
 * @brief       get argument names from the input parameters.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 * @param[in]   optind        #3: current option index
 * @param[out]  names         #4: argument names
 * @param[out]  all           #5: set to TRUE if all is specified or no argument in some cases
 * @param[in]   cmd           #6: command, if no argument is specified,
 *                                set all to TRUE according to cmd
 *
 * @retval      0             #1: set all to TRUE
 * @retval      > 0           #2: number of specified argument names
 */
unsigned int
get_argument_names(int argc, char **argv, int optind, char ***names,
                   int *all, const char *cmd)
{
    int size=16;
    char **nameList;
    nameList = (char **)calloc(size, sizeof(char *));
    if (NULL == nameList) {
        perror("calloc");
        exit(4);
    }

    int numName = 0;
    *all = FALSE;
    if (argc >= optind+1) {

        for (numName = 0; argc > optind; optind++) {

            if ((0 == strcmp(cmd, "hostC") || 0 == strcmp(cmd, "queueC")
                 || 0 == strcmp(cmd, "user") || 0 == strcmp(cmd, "cluster")
                 || 0 == strcmp(cmd, "queue") || 0 == strcmp(cmd, "resource")
                 || 0 == strcmp(cmd, "group") || 0 == strcmp(cmd, "host"))
                && strcmp(argv[optind], ALL_STRING) == 0) {

                *all = TRUE;
                numName = 0;
                break;
            }

            if (has_name_inlist(argv[optind], nameList, numName)) {
                continue;
            }

            if (0 != size && numName >= size) {

                size <<= 1;

                char **tmpList;
                if (NULL == (tmpList=(char **)realloc(nameList, size*sizeof(char *)))) {
                    *names = nameList;
                    return (numName);
                }

                nameList = tmpList;
            }

            nameList[numName] = argv[optind];
            numName ++;
        }
    } else if (argc == optind) {
        if (0 == strcmp(cmd, "hostC")) {
            nameList[numName] = qp_getmyhostname();
            numName ++;
        }
    }

    *names = nameList;

    return (numName);
} // end function get_argument_names

/**
 * @brief       get list of job IDs from argv.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 * @param[out]  outjobid      #2: the job ID got from argument
 *
 * @retval      > 0           #1: succeed
 * @retval      0             #2: no job ID spefified
 * @retval      -1            #3: failed, not enough memory
 */
int
get_jobids_fromargv(int argc, char **argv, JOBID_T **outjobid)
{
    if (NULL == outjobid || NULL != *outjobid) {
        return 0;
    }

    if (argc < optind + 1) {
        return 0;
    }

    int maxNumJobIds=MAX_JOB_IDS;
    JOBID_T *jobIds;
    jobIds = (JOBID_T *)calloc(maxNumJobIds, sizeof(JOBID_T));
    if (NULL == jobIds) {
        perror("calloc");
        exit(4);
    }

    int numJobIds=0;
    for ( ; argc>optind; optind++) {

        JOBID_T oneJobId;
        struct array_index *jobArrayIdx=NULL;
        if (get_jobid_indicies(argv[optind], &oneJobId, &jobArrayIdx)) {
            exit(2);
        }

        if (NULL == jobArrayIdx) {

            if (numJobIds >= maxNumJobIds) {
                maxNumJobIds += MAX_JOB_IDS;

                jobIds = (JOBID_T *)realloc(jobIds, maxNumJobIds*sizeof(JOBID_T));
                if (NULL == jobIds) {
                    fprintf(stderr, STR_FUNC_FAIL_M, __func__, "realloc");
                    exit(-1);
                }
            }

            if (0 == oneJobId) {
                numJobIds = 0;
                break;
            }

            int i;
            for (i=0; i<numJobIds; i++) {
                if (oneJobId == jobIds[i]) {
                    break;
                }
            }

            if (i == numJobIds) {
                jobIds[numJobIds] = oneJobId;
                numJobIds ++;
            }

            continue;
        }

        struct array_index *loopIdx;
        for (loopIdx=jobArrayIdx; NULL!=loopIdx; loopIdx=loopIdx->next_index) {

            int j;
            for(j=loopIdx->start; j<=loopIdx->end; j+=loopIdx->step) {
                JOBID_T combineJobId;

                combineJobId = QP_JOBID(oneJobId, j);

                if (numJobIds >= maxNumJobIds) {

                    maxNumJobIds += MAX_JOB_IDS;
                    jobIds = (JOBID_T *)realloc(jobIds, maxNumJobIds*sizeof(JOBID_T));
                    if (NULL == jobIds) {
                        fprintf(stderr, STR_FUNC_FAIL_M, __func__, "realloc");
                        exit(4);
                    }
                }

                int i;
                for (i=0; i<numJobIds; i++) {
                    if (combineJobId == jobIds[i]) {
                        break;
                    }
                }

                if (i == numJobIds) {
                    jobIds[numJobIds] = combineJobId;
                    numJobIds ++;
                }
            }
        }

        free_jobarray_idx(jobArrayIdx);
    }

    *outjobid = jobIds;

    return (numJobIds);
} // end function get_jobids_fromargv

/**
 * @brief       get one job ID from the input string.
 *
 * @param[in]   string        #1: transfer this string to job ID
 * @param[out]  outjobid      #2: the job ID got from the input string
 *
 * @retval      0             #1: succeed
 * @retval      -1            #2: failed, maybe specified an array job
 *
 * @note  jobID examples: 123, 123[3]
 */
int
get_one_jobid(char *string, JOBID_T *outjobid)
{
    JOBID_T jobId = 0;
    struct array_index *jobArrayIdx = NULL;

    if (get_jobid_indicies(string, &jobId, &jobArrayIdx)) {
        return -1;
    }

    if (0 == jobId) {
        fprintf(stderr, "0 is not a job ID.\n");
        free_jobarray_idx(jobArrayIdx);
        return -1;
    }


    if (NULL == jobArrayIdx) { // not specify job array index.
        *outjobid = jobId;
        return 0;
    }


    if ((NULL != jobArrayIdx->next_index)
        || (jobArrayIdx->start != jobArrayIdx->end) || (jobArrayIdx->step != 1)) {

        fprintf(stderr, "%s: Must specify one element of an array job.\n", string);
        free_jobarray_idx(jobArrayIdx);
        return -1;
    }


    *outjobid = QP_JOBID(jobId, jobArrayIdx->start);
    free_jobarray_idx(jobArrayIdx);

    return 0;
} // end function get_one_jobid

const char *
get_post_status(struct QuickPoolJobInfo *job)
{
    const char *status;
    int jobStatus = MASK_STATUS(job->status);

    switch (jobStatus & (JOB_STATUS_PDONE | JOB_STATUS_PERR)) {
    case JOB_STATUS_PDONE:
        status = "POST_DONE";
        break;
    case JOB_STATUS_PERR:
        status = "POST_ERR";
        break;
    default:
        status = "NOTPOST";
        break;
    }

    if (IS_JOB_POST_DONE(jobStatus) || IS_JOB_POST_ERR(jobStatus)) {
        return status;
    }

    return NULL;
} // end function get_post_status

const char *
get_status(struct QuickPoolJobInfo *job)
{
    const char *status;
    int jobStatus = MASK_STATUS(job->status);

    if (jobStatus & JOB_STATUS_UNKWN) {
        status = "UNKWN";
        return status;
    }

    switch (jobStatus) {
    case 0:
        status = "NULL";
        break;
    case JOB_STATUS_PEND:
        status = "PEND";
        break;
    case JOB_STATUS_PSUSP:
        status = "PSUSP";
        break;
    case JOB_STATUS_RUN:
        status = "RUN";
        break;
    case JOB_STATUS_SSUSP:
        status = "SSUSP";
        break;
    case JOB_STATUS_USUSP:
        status = "USUSP";
        break;
    case JOB_STATUS_EXIT|JOB_STATUS_PERR:
    case JOB_STATUS_EXIT|JOB_STATUS_PDONE:
    case JOB_STATUS_EXIT:
        if (job->reason_id & EXIT_ZOMBIE)
	    status = "ZOMBI";
        else
            status = "EXIT";
        break;
    case JOB_STATUS_DONE|JOB_STATUS_PERR:
    case JOB_STATUS_DONE|JOB_STATUS_PDONE:
    case JOB_STATUS_DONE:
        status = "DONE";
        break;
    default:
        status = "ERROR";
    }

    return status;
} // end function get_status
